<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<div style="width: 100%; height: 2000px;"></div>
		<script>
			// https://www.cnblogs.com/ndood/p/12602746.html
			{
				//  单例模式
				function SetManager(name) {
					this.manager = name;
				}
				SetManager.prototype.getName = function() {
					console.log(this.manager);
				};
				// var SingletonSetManager = (function() {
				//     var manager = null;
				//     return function(name) {
				//         if (!manager) {
				//             manager = new SetManager(name);
				//         }
				//         return manager;
				//     } 
				// })();
				// SingletonSetManager('a').getName(); // a
				// SingletonSetManager('b').getName(); // a
				// SingletonSetManager('c').getName(); // a

				// 提取出通用的单例
				function getSingleton(fn) {
					var instance = null;
					return function() {
						if (!instance) {
							instance = fn.apply(this, arguments);
						}
						return instance;
					}
				}

				// 获取单例
				var managerSingleton = getSingleton(function(name) {
					var manager = new SetManager(name);
					return manager;
				});
				managerSingleton('a').getName(); // a
				managerSingleton('b').getName(); // a
				managerSingleton('c').getName(); // a

				function SetHr(name) {
					this.hr = name;
				}
				SetHr.prototype.getName = function() {
					console.log(this.hr);
				};
				var hrSingleton = getSingleton(function(name) {
					var hr = new SetHr(name);
					return hr;
				});
				hrSingleton('aa').getName(); // aa
				hrSingleton('bb').getName(); // aa
				hrSingleton('cc').getName(); // aa

				function createPopup(html) {
					var div = document.createElement('div');
					div.innerHTML = html;
					document.body.append(div);
					return div;
				}
				var popupSingleton = getSingleton(function() {
					var div = createPopup.apply(this, arguments);
					return div;
				});
				console.log(
					popupSingleton('aaa').innerHTML,
					popupSingleton('bbb').innerHTML,
					popupSingleton('bbb').innerHTML
				); // aaa  aaa  aaa
			}

			{
				// 策略模式
				// 加权映射关系
				var levelMap = {
					S: 10,
					A: 8,
					B: 6,
					C: 4
				};
				// 组策略
				var scoreLevel = {
					basicScore: 80,
					S: function() {
						return this.basicScore + levelMap['S'];
					},
					A: function() {
						return this.basicScore + levelMap['A'];
					},
					B: function() {
						return this.basicScore + levelMap['B'];
					},
					C: function() {
						return this.basicScore + levelMap['C'];
					}
				}
				// 调用
				function getScore(level) {
					return scoreLevel[level] ? scoreLevel[level]() : 0;
				}
				console.log(
					getScore('S'),
					getScore('A'),
					getScore('B'),
					getScore('C'),
					getScore('D')
				); // 90 88 86 84 0

				// 错误提示
				var errorMsgs = {
					default: '输入数据格式不正确',
					minLength: '输入数据长度不足',
					isNumber: '请输入数字',
					required: '内容不为空'
				};
				// 规则集
				var rules = {
					minLength: function(value, length, errorMsg) {
						if (value.length < length) {
							return errorMsg || errorMsgs['minLength']
						}
					},
					isNumber: function(value, errorMsg) {
						if (!/\d+/.test(value)) {
							return errorMsg || errorMsgs['isNumber'];
						}
					},
					required: function(value, errorMsg) {
						if (value === '') {
							return errorMsg || errorMsgs['required'];
						}
					}
				};
				// 校验器
				function Validator() {
					this.items = [];
				};
				Validator.prototype = {
					constructor: Validator,
					// 添加校验规则
					add: function(value, rule, errorMsg) {
						var arg = [value];
						if (rule.indexOf('minLength') !== -1) {
							var temp = rule.split(':');
							arg.push(temp[1]);
							rule = temp[0];
						}
						arg.push(errorMsg);
						this.items.push(function() {
							// 进行校验
							return rules[rule].apply(this, arg);
						});
					},
					// 开始校验
					start: function() {
						for (var i = 0; i < this.items.length; ++i) {
							var ret = this.items[i]();
							if (ret) {
								console.log(ret, 'errorMsg');
								// return ret;
							}
						}
					}
				};
				// 测试数据
				function testTel(val) {
					return val;
				}
				var validate = new Validator();
				validate.add(testTel('ccc'), 'isNumber', '只能为数字'); // 只能为数字
				validate.add(testTel(''), 'required'); // 内容不为空
				validate.add(testTel('123'), 'minLength:5', '最少5位'); // 最少5位
				validate.add(testTel('12345'), 'minLength:5', '最少5位');
				var ret = validate.start();
			}

			{
				// 代理模式
				// 保护代理
				// 主体，发送消息
				function sendMsg(msg) {
					console.log(msg);
				}
				// 代理，对消息进行过滤
				function proxySendMsg(msg) {
					// 无消息则直接返回
					if (typeof msg === 'undefined') {
						console.log('deny');
						return;
					}
					// 有消息则进行过滤
					msg = ('' + msg).replace(/泥\s*煤/g, '');
					sendMsg(msg);
				}
				sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀
				proxySendMsg('泥煤呀泥 煤'); // 呀
				proxySendMsg(); // deny


				// 虚拟代理
				// 函数防抖，频繁操作中不处理，直到操作完成之后（再过 delay 的时间）才一次性处理
				function debounce(fn, delay) {
					delay = delay || 200;
					var timer = null;
					return function() {
						var arg = arguments;
						// 每次操作时，清除上次的定时器
						clearTimeout(timer);
						timer = null;
						// 定义新的定时器，一段时间后进行操作
						timer = setTimeout(function() {
							fn.apply(this, arg);
						}, delay);
					}
				};
				var count = 0;
				// 主体
				function scrollHandle(e) {
					console.log(e.type, ++count); // scroll
				}
				// 代理
				var proxyScrollHandle = (function() {
					return debounce(scrollHandle, 500);
				})();
				window.onscroll = proxyScrollHandle;

				// 缓存代理
				// 主体
				function add() {
					var arg = [].slice.call(arguments);
					return arg.reduce(function(a, b) {
						return a + b;
					});
				}
				// 代理
				var proxyAdd = (function() {
					var cache = [];
					return function() {
						var arg = [].slice.call(arguments).join(',');
						// 如果有，则直接从缓存返回
						if (cache[arg]) {
							return cache[arg];
						} else {
							var ret = add.apply(this, arguments);
							return ret;
						}
					};
				})();
				console.log(
					add(1, 2, 3, 4),
					add(1, 2, 3, 4),
					proxyAdd(10, 20, 30, 40),
					proxyAdd(10, 20, 30, 40)
				); // 10 10 100 100
			}

			{
				// 发布-订阅模式
				// 订阅
				document.body.addEventListener('click', function() {
					console.log('click1');
				}, false);
				document.body.addEventListener('click', function() {
					console.log('click2');
				}, false);
				// 发布
				document.body.click(); // click1  click2

				// 观察者
				var observer = {
					// 订阅集合
					subscribes: [],
					// 订阅
					subscribe: function(type, fn) {
						if (!this.subscribes[type]) {
							this.subscribes[type] = [];
						}
						// 收集订阅者的处理
						typeof fn === 'function' && this.subscribes[type].push(fn);
					},
					// 发布  可能会携带一些信息发布出去
					publish: function() {
						var type = [].shift.call(arguments),
							fns = this.subscribes[type];
						// 不存在的订阅类型，以及订阅时未传入处理回调的
						if (!fns || !fns.length) {
							return;
						}
						// 挨个处理调用
						for (var i = 0; i < fns.length; ++i) {
							fns[i].apply(this, arguments);
						}
					},
					// 删除订阅
					remove: function(type, fn) {
						// 删除全部
						if (typeof type === 'undefined') {
							this.subscribes = [];
							return;
						}
						var fns = this.subscribes[type];
						// 不存在的订阅类型，以及订阅时未传入处理回调的
						if (!fns || !fns.length) {
							return;
						}
						if (typeof fn === 'undefined') {
							fns.length = 0;
							return;
						}
						// 挨个处理删除
						for (var i = 0; i < fns.length; ++i) {
							if (fns[i] === fn) {
								fns.splice(i, 1);
							}
						}
					}
				};
				// 订阅岗位列表
				function jobListForA(jobs) {
					console.log('A', jobs);
				}
				function jobListForB(jobs) {
					console.log('B', jobs);
				}
				// A订阅了笔试成绩
				observer.subscribe('job', jobListForA);
				// B订阅了笔试成绩
				observer.subscribe('job', jobListForB);
				// A订阅了笔试成绩
				observer.subscribe('examinationA', function(score) {
					console.log(score);
				});
				// B订阅了笔试成绩
				observer.subscribe('examinationB', function(score) {
					console.log(score);
				});
				// A订阅了面试结果
				observer.subscribe('interviewA', function(result) {
					console.log(result);
				});
				observer.publish('examinationA', 100); // 100
				observer.publish('examinationB', 80); // 80
				observer.publish('interviewA', '备用'); // 备用
				observer.publish('job', ['前端', '后端', '测试']); // 输出A和B的岗位
				// B取消订阅了笔试成绩
				observer.remove('examinationB');
				// A都取消订阅了岗位
				observer.remove('job', jobListForA);
				observer.publish('examinationB', 80); // 没有可匹配的订阅，无输出
				observer.publish('job', ['前端', '后端', '测试']); // 输出B的岗位
			}

			{
				// 中介者模式
				var A = {
					score: 10,
					changeTo: function(score) {
						this.score = score;
						// 自己获取
						this.getRank();
					},
					// 直接获取
					getRank: function() {
						var scores = [this.score, B.score, C.score].sort(function(a, b) {
							return a < b;
						});
						console.log(scores, scores.indexOf(this.score) + 1, 'intermediary');
					}
				};
				var B = {
					score: 20,
					changeTo: function(score) {
						this.score = score;
						// 通过中介者获取
						rankMediator(B);
					}
				};
				var C = {
					score: 30,
					changeTo: function(score) {
						this.score = score;
						rankMediator(C);
					}
				};
				// 中介者，计算排名
				function rankMediator(person) {
					var scores = [A.score, B.score, C.score].sort(function(a, b) {
						return a < b;
					});
					console.log(scores, scores.indexOf(person.score) + 1, 'intermediary');
				}
				// A通过自身来处理
				A.changeTo(100); // 1
				// B和C交由中介者处理
				B.changeTo(200); // 2
				C.changeTo(50); // 3
			}
		</script>
	</body>
</html>